package lex.dm.ui.dt;

import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingWorker;
import javax.swing.tree.TreeNode;

import org.lex.swing.ActionBuilder;
import org.lex.swing.Swings;
import org.lex.swing.util.Buttons;
import org.lex.swing.util.CenterSouthPane;
import org.lex.swing.widgets.EnhancedDialog;
import org.lex.utils.Pair;

import lex.dm.DTree;
import lex.dm.id3.*;
import lex.dm.ui.ProgressDialog;

public class DTUtils
{
	private static Pair<? extends IDataSet, ? extends IDataSet> dataSet = null;
	private static ID3Node rootNode = null;
	private static final boolean DEBUG = DecisionTreeDialog.DEBUG;
	private static final int COUNT = 10;

	public static void applyClassification( final Window owner, final ID3Node root,
		final IDataSet dataSet )
	{
		final ProgressDialog d = new ProgressDialog( owner );
		d.setTitle( "ID3 Classification" );
		d.setPrompt( "Classifying on " + dataSet.getRowCount() + " samples" );
		d.setMessage( "Please wait..." );
		new SwingWorker<Integer, Void>(){
			@Override
			protected Integer doInBackground() throws Exception
			{
				return new ID3Classification().classify( root, dataSet );
			}

			@Override
			protected void done()
			{
				d.dispose();
				// ----------
				try
				{
					int valid = this.get();
					int total = dataSet.getRowCount();
					StringBuilder buf = new StringBuilder();
					buf.append( "<html>" );
					buf.append( valid ).append( " of " ).append( total );
					buf.append( " samples hit.<br>" );
					buf.append( "��ȷ�� " );
					buf.append( Math.round( valid * 10000 / total ) / 100F );
					buf.append( "%<html>" );
					JOptionPane.showMessageDialog( owner, buf.toString(),
						"Classification Finished", JOptionPane.INFORMATION_MESSAGE );
				}
				catch( Exception e )
				{
					e.printStackTrace();
				}
			}
		}.execute();
		d.setLocationRelativeTo( owner );
		d.setVisible( true );
	}

	public static ID3Node generateDT( final Window root, final IDataSet dataSet )
	{
		final ProgressDialog d = new ProgressDialog( root );
		d.setTitle( "Decision Tree" );
		d.setPrompt( "Generateing decision tree from " + dataSet.getRowCount() + " samples..." );
		d.setMessage( "Please wait..." );
		d.setLocationRelativeTo( root );
		final ID3 id3 = new ID3( dataSet );
		new SwingWorker<ID3Node, Void>(){
			@Override
			protected ID3Node doInBackground() throws Exception
			{
				return id3.createTree();
			}

			@Override
			protected void done()
			{
				try
				{
					d.dispose();
					rootNode = this.get();
					EnhancedDialog d = createDTDialog( root, rootNode );
					d.setTitle( "Decision Tree from " + dataSet.getRowCount() + " samples" );
					d.setVisible( true );
				}
				catch( Exception e )
				{
					e.printStackTrace();
				}
			}
		}.execute();
		d.setVisible( true );
		return rootNode;
	}

	/**
	 * ִ��sql������ݿ��ж�����ѯ�����������ݼ�����������ݼ������������
	 * 
	 * @param root
	 * @param sql
	 * @return
	 */
	public static Pair<? extends IDataSet, ? extends IDataSet> loadDataSet( final Window root,
		final String sql )
	{
		final ProgressDialog d = new ProgressDialog( root );
		d.setTitle( "Training Set" );
		d.setPrompt( "Load training set from database..." );
		d.setMessage( "Please wait..." );
		new SwingWorker<Void, Void>(){
			@Override
			protected Void doInBackground() throws Exception
			{
				dataSet = createDataSet( sql );
				return null;
			}

			@Override
			protected void done()
			{
				d.dispose();
			}
		}.execute();
		d.setLocationRelativeTo( root );
		d.setVisible( true );
		return dataSet;
	}

	public static EnhancedDialog createDTDialog( Window root, TreeNode node )
	{
		final EnhancedDialog d = new EnhancedDialog( root, "Decision Tree" );
		JTree tree = new JTree( node );
		Swings.expandAll( tree );
		Buttons b = Buttons.center( new ActionBuilder( "Close" )
			.setActionListener( new ActionListener(){
				@Override
				public void actionPerformed( ActionEvent e )
				{
					d.dispose();
				}
			} ) );
		d.setDefaultButton( b.getButton( 0 ) );
		d.setContentPane( new CenterSouthPane( new JScrollPane( tree ), b ) );
		// d.setSize( 300, 320 );
		d.pack();
		d.setLocationRelativeTo( root );
		return d;
	}

	/**
	 * sql�е����һ����CLASS
	 * 
	 * @param sql
	 * @return
	 * @throws SQLException
	 */
	public static Pair<? extends IDataSet, ? extends IDataSet> createDataSet( String sql )
		throws SQLException
	{
		ResultSet rs = DTree.getApplication().getConnection().createStatement().executeQuery( sql );
		ResultSetMetaData md = rs.getMetaData();
		int columnCount = md.getColumnCount();
		// load attributes
		int classAttrIndex = columnCount - 1;
		String[] attrs = new String[columnCount];
		for( int col = 1; col <= columnCount; col++ )
			attrs[col - 1] = md.getColumnLabel( col );
		// load data
		int count = 0;
		ArrayList<Object[]> data = new ArrayList<Object[]>();
		while( rs.next() )
		{
			Object[] row = new Object[columnCount];
			for( int i = 0; i < row.length; i++ )
			{
				row[i] = rs.getObject( i + 1 );
				if( DEBUG )
				{
					if( null == row[i] )
						System.out.print( " , " );
					else
						System.out.print( row[i].getClass().getName() + ", " );
					if( i == row.length - 1 )
						System.out.println();
				}
			}
			data.add( row );
			count++;
			if( DEBUG )
			{
				// ֻѡȡ�ٲ��������Ϊ����
				if( rs.next() )
					rs.next();
				if( count > COUNT )
					break;
			}
		}
		int split = data.size() / 2;
		List<Object[]> a = data.subList( 0, split );
		List<Object[]> b = data.subList( split, data.size() );
		return Pair.wrap( new ArrayDataSet( a.toArray( new Object[0][] ), attrs, classAttrIndex ),
			new ArrayDataSet( b.toArray( new Object[0][] ), attrs, classAttrIndex ) );
		// return new ArrayDataSet( data.toArray( new Object[0][] ), attrs,
		// classAttrIndex );
	}
}
